﻿/*   
    Copyright (C) 2009 Galaktika Corporation ZAO

    This file is a part of Galaktika.BusinessMonitor
 
    Galaktika.BusinessMonitor is a free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
      
    You should have received a copy of the GNU General Public License
    along with Galaktika.BusinessMonitor.  If not, see <http://www.gnu.org/licenses/>.
  
    If GPL v.3 is not suitable for your products or company,
    Galaktika Corp provides Galaktika.BusinessMonitor under a flexible commercial license
    designed to meet your specific usage and distribution requirements.
    If you have already obtained a commercial license from Galaktika Corp,
    you can use this file under those license terms.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

using Microsoft.AnalysisServices.AdomdClient;
using Galaktika.BI.Silverlight.Services;
using Ranet.Olap.Core.Data;

namespace Galaktika.BI.Silverlight.ClientApp.Web.Runtime.DataAccess
{
    public class AdomdQueryExecutor : IQueryExecutor
    {
        public AdomdQueryExecutor()
        {
        }

        #region IQueryExecutor Members

        AdomdCommand m_CurrentCmd;
        public InvokeResultSchema ExecuteQuery(string connectionString, string query, string sessionId)
        {
            if (sessionId == string.Empty)
            {
                sessionId = null;
            }
            InvokeResultSchema res = new InvokeResultSchema();
            try
            {
                res.ContentType = KnownContentTypes.MultidimData;
                using (m_CurrentCmd = new AdomdCommand(query))
                {
                    CellSetData csd = StaticExecuteQuery(connectionString, m_CurrentCmd, ref sessionId);
                    res.Content = CellSetData.Serialize(csd);
                }
                lock (this)
                {
                    m_CurrentCmd = null;
                }
            }
            catch (AdomdConnectionException connection_ex)
            {
                // Установка ADOMDError приведет к зависанию
                res.ContentType = KnownContentTypes.ADOMDError;
                res.Content = connection_ex.Message;
            }
            catch (AdomdErrorResponseException response_ex)
            {
                // Установка ADOMDError приведет к зависанию
                res.ContentType = KnownContentTypes.ADOMDError;
                res.Content = response_ex.Message;
            }
            catch (InvalidOperationException invalid_op)
            {
                res.ContentType = KnownContentTypes.Error;
                if (invalid_op.InnerException is OutOfMemoryException)
                    res.Content = Localization.Msg_OutOfMemory;
                else
                    res.Content = invalid_op.Message;
            }
            catch (Exception ex)
            {
                res.ContentType = KnownContentTypes.Error;
                res.Content = ex.Message;
            }
            res.SessionId = sessionId;

            return res;
        }

        public InvokeResultSchema ExecuteNonQuery(string connectionString, string query, string sessionId)
        {
            if (sessionId == string.Empty)
            {
                sessionId = null;
            }
            InvokeResultSchema res = new InvokeResultSchema();
            try
            {
                res.ContentType = KnownContentTypes.MultidimData;
                using (m_CurrentCmd = new AdomdCommand(query))
                {
                    res.Content = StaticExecuteNonQuery(connectionString, m_CurrentCmd, ref sessionId).ToString();
                }
            }
            catch (InvalidOperationException invalid_op)
            {
                res.ContentType = KnownContentTypes.Error;
                if (invalid_op.InnerException is OutOfMemoryException)
                    res.Content = Localization.Msg_OutOfMemory;
                else
                    res.Content = invalid_op.Message;
            }
            catch (Exception ex)
            {
                res.ContentType = KnownContentTypes.Error;
                res.Content = ex.Message;
            }
            res.SessionId = sessionId;

            return res;
        }

        public static void OpenConnection(AdomdConnection conn, string sessionId)
        {
            OpenConnection(conn, sessionId, sessionId != null);
        }

        private static void OpenConnection(AdomdConnection conn, string sessionId, bool tryInNewSession)
        {
            conn.SessionID = sessionId;
            try
            {
                conn.Open();
            }
            catch (AdomdException)
            {
                if (tryInNewSession)
                {
                    OpenConnection(conn, null, false);
                }
                else
                {
                    throw;
                }
            }
        }

        public bool Cancel()
        {
            lock (this)
            {
                if (m_CurrentCmd != null)
                {
                    m_CurrentCmd.Cancel();

                    return true;
                }
            }

            return false;
        }

        #endregion

        public static int StaticExecuteNonQuery(string connectionString, string query)
        {
            string sessionId = null;
            return StaticExecuteNonQuery(connectionString, query, ref sessionId);
        }

        public static int StaticExecuteNonQuery(string connectionString, string query, ref string sessionId)
        {
            int res = -1;
            if (!String.IsNullOrEmpty(connectionString) && !String.IsNullOrEmpty(query))
            {
                var conn = AdomdConnectionPool.GetConnection(connectionString, sessionId);
                {
                    sessionId = conn.SessionID;

                    try
                    {
                        using (AdomdCommand cmd = new AdomdCommand(query, conn))
                        {
                            res = StaticExecuteNonQueryCore(conn, cmd);
                        }
                    }
                    catch (AdomdCacheExpiredException)
                    {
                        conn.Close();
                        AdomdQueryExecutor.OpenConnection(conn, null);
                        sessionId = conn.SessionID;

                        using (AdomdCommand cmd = new AdomdCommand(query, conn))
                        {
                            res = StaticExecuteNonQueryCore(conn, cmd);
                        }
                    }
                }
            }
            return res;
        }

        public static int StaticExecuteNonQuery(string connectionString, AdomdCommand cmd)
        {
            var sid = (string)null;
            return StaticExecuteNonQuery(connectionString, cmd, ref sid);
        }

        public static int StaticExecuteNonQuery(string connectionString, AdomdCommand cmd, ref string sessionId)
        {
            int res = -1;
            if (!String.IsNullOrEmpty(connectionString) && !String.IsNullOrEmpty(cmd.CommandText))
            {
                var conn = AdomdConnectionPool.GetConnection(connectionString, sessionId);
                {
                    sessionId = conn.SessionID;

                    try
                    {
                        res = StaticExecuteNonQueryCore(conn, cmd);
                    }
                    catch (AdomdCacheExpiredException)
                    {
                        conn.Close();
                        AdomdQueryExecutor.OpenConnection(conn, null);
                        sessionId = conn.SessionID;

                        res = StaticExecuteNonQueryCore(conn, cmd);
                    }
                }
            }
            return res;
        }

        private static int StaticExecuteNonQueryCore(AdomdConnection conn, AdomdCommand cmd)
        {
            cmd.Connection = conn;
            return cmd.ExecuteNonQuery();
        }

        public static CellSetData StaticExecuteQuery(string connectionString, string query)
        {
            var sid = (string)null;
            return StaticExecuteQuery(connectionString, query, ref sid);
        }

        public static CellSetData StaticExecuteQuery(string connectionString, string query, ref string sessionId)
        {
            CellSetData res = null;
            if (!String.IsNullOrEmpty(connectionString))
            {
                var conn = AdomdConnectionPool.GetConnection(connectionString, sessionId);
                {
                    sessionId = conn.SessionID;

                    try
                    {
                        using (var cmd = new AdomdCommand(query, conn))
                        {
                            res = StaticExecuteQueryCore(conn, cmd);
                        }
                    }
                    catch (AdomdCacheExpiredException)
                    {
                        conn.Close();
                        AdomdQueryExecutor.OpenConnection(conn, null);
                        sessionId = conn.SessionID;

                        using (var cmd = new AdomdCommand(query, conn))
                        {
                            res = StaticExecuteQueryCore(conn, cmd);
                        }
                    }
                }
            }
            return res;
        }

        public static CellSetData StaticExecuteQuery(string connectionString, AdomdCommand cmd)
        {
            string sessionId = null;
            return StaticExecuteQuery(connectionString, cmd, ref sessionId);
        }

        public static CellSetData StaticExecuteQuery(string connectionString, AdomdCommand cmd, ref string sessionId)
        {
            CellSetData res = null;
            if (!String.IsNullOrEmpty(connectionString))
            {
                var conn = AdomdConnectionPool.GetConnection(connectionString, sessionId);
                {
                    sessionId = conn.SessionID;

                    try
                    {
                        res = StaticExecuteQueryCore(conn, cmd);
                    }
                    catch (AdomdCacheExpiredException)
                    {
                        conn.Close();
                        AdomdQueryExecutor.OpenConnection(conn, null);
                        sessionId = conn.SessionID;

                        res = StaticExecuteQueryCore(conn, cmd);
                    }
                }
            }
            return res;
        }

        private static CellSetData StaticExecuteQueryCore(AdomdConnection conn, AdomdCommand cmd)
        {
            if (conn == null)
                throw new ArgumentNullException("conn");
            CellSetData res = null;

            // Пустой запрос не будем выполнять
            if (!string.IsNullOrEmpty(cmd.CommandText))
            {
                cmd.Connection = conn;
                var cs = cmd.ExecuteCellSet();

                res = new Ranet.Olap.Core.Providers.CellSetDescriptionProvider(cs).CellSet;
            }
            else
            {
                res = new CellSetData();
            }

            return res;
        }
    }
}
